package hamaster.jpbc.pbc;

import it.unisa.dia.gas.jpbc.Element;
import it.unisa.dia.gas.jpbc.Pairing;
import it.unisa.dia.gas.plaf.jpbc.pairing.CurveParams;
import it.unisa.dia.gas.plaf.jpbc.pairing.PairingFactory;

/**
 * 对PBC库的Java封装
 * 使用前必须先调用instance(CurveParams)方法初始化参数
 * @author <a href="mailto:wangyeee@gmail.com">Wang Ye</a>
 */
public final class PBC {

	final public static int PBC_G_LENGTH = 128;
	final public static int PBC_ZR_LENGTH = 20;

	private static PBC singleton;

	private Pairing pairing;

	private PBC() {
		pairing = null;
	}

	static {
		if (singleton == null)
			singleton = new PBC();
	}

	/**
	 * 获取PBC的单一实例
	 * @return PBC实例 如果未初始化 则返回null
	 */
	public static PBC instance() {
		if (singleton.pairing == null)
			return null;
		return singleton;
	}

	/**
	 * 获取PBC的单一实例
	 * @param params 选取的椭圆曲线参数
	 * @return PBC实例
	 */
	public static PBC instance(CurveParams params) {
		if (singleton.pairing == null)
			singleton.pairing = PairingFactory.getPairing(params);
		return singleton;
	}

	/**
	 * 解密
	 * @param cipher 待解密的密文
	 * @param key 解密用私钥
	 * @return 解密后的明文
	 */
	public PlainText decrypt(CipherText cipher, PrivateKey key) {
		Element e1 = pairing.pairing(cipher.getU(), key.gethID());
		Element e2 = cipher.getV().duplicate();
		e2.powZn(key.getrID());
		Element plain = cipher.getW().duplicate();
		plain.mul(e1);
		plain.mul(e2);
		PlainText p = new PlainText();
		p.setElement(plain);
		p.setLength(cipher.getLength());

		// TODO 擦除内存中敏感数据
		e2.setToRandom();
		e1.setToRandom();

		return p;
	}

	/**
	 * 加密
	 * @param message 待加密的明文
	 * @param user 接收者公钥 即身份标识ID
	 * @param parameter 系统公共参数
	 * @return 加密后的密文
	 */
	public CipherText encrypt(PlainText message, Element user, PublicParameter parameter) {
		Element u = parameter.getG1().duplicate();
		Element w = message.getElement().duplicate();
		Element eg = pairing.getG2().newElement();
		Element rs = pairing.getZr().newRandomElement();
		u.powZn(rs);
		Element e5 = rs.duplicate();
		e5.mul(user);
		Element e4 = parameter.getG().duplicate();
		e4.powZn(e5);
		e4.invert();
		u.mul(e4);
		eg.set(parameter.getG().duplicate());
		Element v = pairing.pairing(parameter.getG().duplicate(), eg);
		v.powZn(rs);
		Element e2 = pairing.pairing(parameter.getG(), parameter.getH());
		e2.powZn(rs);
		e2.invert();
		w.mul(e2);
		CipherText c = new CipherText();
		c.setU(u);
		c.setV(v);
		c.setW(w);
		c.setLength(message.getLength());

		eg.setToRandom();
		rs.setToRandom();
		e5.setToRandom();
		e4.setToRandom();
		e2.setToRandom();

		return c;
	}

	/**
	 * 生成私钥
	 * @param user 用户ID
	 * @param rID
	 * @param alpha
	 * @param parameter
	 * @return
	 */
	public PrivateKey keygen(Element user, Element rID, Element alpha, PublicParameter parameter) {
		Element e1 = alpha.duplicate();
		Element e2 = parameter.getG().duplicate();
		Element hID = parameter.getH().duplicate();
		e1.sub(user);
		e1.invert();
		e2.powZn(rID);
		e2.invert();
		hID.mul(e2);
		hID.powZn(e1);
		PrivateKey pk = new PrivateKey();
		pk.sethID(hID);
		pk.setrID(rID);

		e1.setToRandom();
		e2.setToRandom();

		return pk;
	}

	public PublicParameter setup(Element alpha) {
		alpha.set(pairing.getZr().newRandomElement());
		Element g = pairing.getG1().newRandomElement();
		Element h = pairing.getG2().newRandomElement();
		Element g1 = g.duplicate();
		g1.powZn(alpha);
		PublicParameter pp = new PublicParameter();
		pp.setG(g);
		pp.setG1(g1);
		pp.setH(h);
		return pp;
	}

	public Pairing getPairing() {
		return pairing;
	}

}
